package com.ls.spring.core;

import com.ls.spring.demo.DemoConfig;
import com.ls.spring.demo.biz.Userbiz;
import com.ls.spring.demo.web.UserAction;
import com.sun.crypto.provider.HmacPKCS12PBESHA1;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * 模拟实现基于注解的ioc容器
 */
public class AnnotationConfigApplicationContext {
    private File classesDir = null;
    //存储每一个class文件对应的bean的map容器
    private Map<String, Object> mapBean = new HashMap<String, Object>();

    /**
     * 测试自动装配是否成功的main
     * @param args
     * @throws URISyntaxException
     */
    public static void main(String[] args) throws URISyntaxException {
        AnnotationConfigApplicationContext anocxt =
                new AnnotationConfigApplicationContext(DemoConfig.class);
        Userbiz ubiz = anocxt.getBean(Userbiz.class);
        ubiz.say();
    }

    /**
     * 构造方法，通过注解配置类实现ioc容器的创建
     * @param cls
     * @throws URISyntaxException
     */
    public AnnotationConfigApplicationContext(Class cls) throws URISyntaxException{
        //根据@Bean 方法创建 bean
        //扫描业务组件

        //获取类上的组件扫描注解
        ComponentScan cs = (ComponentScan) cls.getAnnotation(ComponentScan.class);
        if(cs != null){
            //获取注解中的值将其存入数组
            String[] value = cs.value();
            //简单模拟--获取其中的一个值
            String basePackage = value[0];
            //将获取到的需要扫描的包将其中的.替换为/
            String basePath = basePackage.replaceAll("\\.", "/");
            //获取类加载器
            ClassLoader classLoader = getClass().getClassLoader();
            //获取该包的地址 url
            URL url = classLoader.getResource(basePath);
            URI uri = url.toURI();
            //构建包对应的文件夹
            File baseDir = new File(uri);

            //保存classes目录路径
            URI uriClass = getClass().getClassLoader().getResource("").toURI();
            classesDir = new File(uriClass);

            System.out.println(classesDir.getAbsolutePath());
            //解析获取文件夹下面的.class文件，并且创建相对应的bean
            scanBean(baseDir);
            System.out.println(mapBean);

            //实现自动装配
            autowired();
        }
    }

    /**
     * 将容器中创建好的bnea自动装匹配好
     */
    private void autowired() {
        //遍历mapBean中所有的bean
        for (Object bean : mapBean.values()) {
            //遍历每一个bena的所有属性
            for(Field fie : bean.getClass().getDeclaredFields()){
                Resource resource = fie.getAnnotation(Resource.class);
                if(resource != null){
                    //默认通过属性名字获取 bean
                    Object value = mapBean.get(fie.getName());
                    //如果根据名字没有找到则根据属性的类型找
                    if(value == null){
                        for(Object b : mapBean.values()){
                            if(b.getClass().equals(fie.getType())){
                                value = b;
                            }
                        }
                    }
                    //强制访问属性
                    fie.setAccessible(true);
                    try {
                        //将找到的bean赋值给 对应属性所在的bena
                        fie.set(bean, value);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    //扫描指定路径下的文件 递归调用
    private void scanBean(File baseDir) {
        for (File file : baseDir.listFiles()) {
            if(file.isFile()){
                //扫描到.class文件后创建相对应的bean
                createBeanByFile(file);
            }else{
                //扫描子目录
                scanBean(file);
            }
        }
    }

    private void createBeanByFile(File file) {
        try {
            //获取相对应的文件绝对路径
            String filePath = file.getAbsolutePath();
            //将后尾部的。class去掉
            filePath = filePath.replace(".class", "");
            //替换掉前半部分保留com后面的
            filePath = filePath.replace(classesDir.getAbsolutePath(), "");
            //替换掉 \
            filePath = filePath.replaceAll("\\\\", ".");
            //截取掉第一个\
            filePath = filePath.substring(1);

            //根据类路径获取类对象
            Class cls = Class.forName(filePath);

            //创建bean
            Object bean = cls.newInstance();

            String name;
            //获取类对象中的 控制器注解
            Controller controller = (Controller) cls.getAnnotation(Controller.class);
            if(controller != null){
                name = controller.value();
                //没有定义名字，那么默认是类名，首字母小写
                if(name.isEmpty()){
                    name = cls.getSimpleName();
                    name = name.substring(0, 1).toLowerCase() + name.substring(1);
                }
                this.mapBean.put(name, bean);
                return;
            }
            //获取类对象中的 服务层注解
            Service Service = (Service) cls.getAnnotation(Service.class);
            if(Service != null){
                name = Service.value();
                //没有定义名字，那么默认是类名，首字母小写
                if(name.isEmpty()){
                    name = cls.getSimpleName();
                    name = name.substring(0, 1).toLowerCase() + name.substring(1);
                }
                this.mapBean.put(name, bean);
                return;
            }
            //获取类对象中的 dao层注解
            Repository repository = (Repository) cls.getAnnotation(Repository.class);
            if(repository != null){
                name = repository.value();
                //没有定义名字，那么默认是类名，首字母小写
                if(name.isEmpty()){
                    name = cls.getSimpleName();
                    name = name.substring(0, 1).toLowerCase() + name.substring(1);
                }
                this.mapBean.put(name, bean);
                return;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取一个bean--根据类对象
     */
    public <T> T getBean(Class<T> cls) {
        for(Object bean : mapBean.values()){
            if(bean.getClass().equals(cls)){
                return (T)bean;
            }
        }
        return null;
    }
}


